ace.define(
  'ace/mode/latex_highlight_rules',
  ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'],
  function (require, exports, module) {
    'use strict';
    var oop = require('../lib/oop');
    var TextHighlightRules = require('./text_highlight_rules').TextHighlightRules;
    var LatexHighlightRules = function () {
      this.$rules = {
        start: [
          {
            token: 'comment',
            regex: '%.*$',
          },
          {
            token: ['keyword', 'lparen', 'variable.parameter', 'rparen', 'lparen', 'storage.type', 'rparen'],
            regex: '(\\\\(?:documentclass|usepackage|input))(?:(\\[)([^\\]]*)(\\]))?({)([^}]*)(})',
          },
          {
            token: ['keyword', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\(?:label|v?ref|cite(?:[^{]*)))(?:({)([^}]*)(}))?',
          },
          {
            token: ['storage.type', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\begin)({)(verbatim)(})',
            next: 'verbatim',
          },
          {
            token: ['storage.type', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\begin)({)(lstlisting)(})',
            next: 'lstlisting',
          },
          {
            token: ['storage.type', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\(?:begin|end))({)([\\w*]*)(})',
          },
          {
            token: 'storage.type',
            regex: /\\verb\b\*?/,
            next: [
              {
                token: ['keyword.operator', 'string', 'keyword.operator'],
                regex: '(.)(.*?)(\\1|$)|',
                next: 'start',
              },
            ],
          },
          {
            token: 'storage.type',
            regex: '\\\\[a-zA-Z]+',
          },
          {
            token: 'lparen',
            regex: '[[({]',
          },
          {
            token: 'rparen',
            regex: '[\\])}]',
          },
          {
            token: 'constant.character.escape',
            regex: '\\\\[^a-zA-Z]?',
          },
          {
            token: 'string',
            regex: '\\${1,2}',
            next: 'equation',
          },
        ],
        equation: [
          {
            token: 'comment',
            regex: '%.*$',
          },
          {
            token: 'string',
            regex: '\\${1,2}',
            next: 'start',
          },
          {
            token: 'constant.character.escape',
            regex: '\\\\(?:[^a-zA-Z]|[a-zA-Z]+)',
          },
          {
            token: 'error',
            regex: '^\\s*$',
            next: 'start',
          },
          {
            defaultToken: 'string',
          },
        ],
        verbatim: [
          {
            token: ['storage.type', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\end)({)(verbatim)(})',
            next: 'start',
          },
          {
            defaultToken: 'text',
          },
        ],
        lstlisting: [
          {
            token: ['storage.type', 'lparen', 'variable.parameter', 'rparen'],
            regex: '(\\\\end)({)(lstlisting)(})',
            next: 'start',
          },
          {
            defaultToken: 'text',
          },
        ],
      };
      this.normalizeRules();
    };
    oop.inherits(LatexHighlightRules, TextHighlightRules);
    exports.LatexHighlightRules = LatexHighlightRules;
  },
);

ace.define(
  'ace/mode/folding/latex',
  ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/folding/fold_mode', 'ace/range', 'ace/token_iterator'],
  function (require, exports, module) {
    'use strict';
    var oop = require('../../lib/oop');
    var BaseFoldMode = require('./fold_mode').FoldMode;
    var Range = require('../../range').Range;
    var TokenIterator = require('../../token_iterator').TokenIterator;
    var keywordLevels = {
      '\\subparagraph': 1,
      '\\paragraph': 2,
      '\\subsubsubsection': 3,
      '\\subsubsection': 4,
      '\\subsection': 5,
      '\\section': 6,
      '\\chapter': 7,
      '\\part': 8,
      '\\begin': 9,
      '\\end': 10,
    };
    var FoldMode = (exports.FoldMode = function () {});
    oop.inherits(FoldMode, BaseFoldMode);
    (function () {
      this.foldingStartMarker = /^\s*\\(begin)|\s*\\(part|chapter|(?:sub)*(?:section|paragraph))\b|{\s*$/;
      this.foldingStopMarker = /^\s*\\(end)\b|^\s*}/;
      this.getFoldWidgetRange = function (session, foldStyle, row) {
        var line = session.doc.getLine(row);
        var match = this.foldingStartMarker.exec(line);
        if (match) {
          if (match[1]) return this.latexBlock(session, row, match[0].length - 1);
          if (match[2]) return this.latexSection(session, row, match[0].length - 1);
          return this.openingBracketBlock(session, '{', row, match.index);
        }
        var match = this.foldingStopMarker.exec(line);
        if (match) {
          if (match[1]) return this.latexBlock(session, row, match[0].length - 1);
          return this.closingBracketBlock(session, '}', row, match.index + match[0].length);
        }
      };
      this.latexBlock = function (session, row, column, returnRange) {
        var keywords = {
          '\\begin': 1,
          '\\end': -1,
        };
        var stream = new TokenIterator(session, row, column);
        var token = stream.getCurrentToken();
        if (!token || !(token.type == 'storage.type' || token.type == 'constant.character.escape')) return;
        var val = token.value;
        var dir = keywords[val];
        var getType = function () {
          var token = stream.stepForward();
          var type = token.type == 'lparen' ? stream.stepForward().value : '';
          if (dir === -1) {
            stream.stepBackward();
            if (type) stream.stepBackward();
          }
          return type;
        };
        var stack = [getType()];
        var startColumn = dir === -1 ? stream.getCurrentTokenColumn() : session.getLine(row).length;
        var startRow = row;
        stream.step = dir === -1 ? stream.stepBackward : stream.stepForward;
        while ((token = stream.step())) {
          if (!token || !(token.type == 'storage.type' || token.type == 'constant.character.escape')) continue;
          var level = keywords[token.value];
          if (!level) continue;
          var type = getType();
          if (level === dir) stack.unshift(type);
          else if (stack.shift() !== type || !stack.length) break;
        }
        if (stack.length) return;
        if (dir == 1) {
          stream.stepBackward();
          stream.stepBackward();
        }
        if (returnRange) return stream.getCurrentTokenRange();
        var row = stream.getCurrentTokenRow();
        if (dir === -1) return new Range(row, session.getLine(row).length, startRow, startColumn);
        else return new Range(startRow, startColumn, row, stream.getCurrentTokenColumn());
      };
      this.latexSection = function (session, row, column) {
        var stream = new TokenIterator(session, row, column);
        var token = stream.getCurrentToken();
        if (!token || token.type != 'storage.type') return;
        var startLevel = keywordLevels[token.value] || 0;
        var stackDepth = 0;
        var endRow = row;
        while ((token = stream.stepForward())) {
          if (token.type !== 'storage.type') continue;
          var level = keywordLevels[token.value] || 0;
          if (level >= 9) {
            if (!stackDepth) endRow = stream.getCurrentTokenRow() - 1;
            stackDepth += level == 9 ? 1 : -1;
            if (stackDepth < 0) break;
          } else if (level >= startLevel) break;
        }
        if (!stackDepth) endRow = stream.getCurrentTokenRow() - 1;
        while (endRow > row && !/\S/.test(session.getLine(endRow))) endRow--;
        return new Range(row, session.getLine(row).length, endRow, session.getLine(endRow).length);
      };
    }.call(FoldMode.prototype));
  },
);

ace.define(
  'ace/mode/latex',
  [
    'require',
    'exports',
    'module',
    'ace/lib/oop',
    'ace/mode/text',
    'ace/mode/latex_highlight_rules',
    'ace/mode/behaviour/cstyle',
    'ace/mode/folding/latex',
  ],
  function (require, exports, module) {
    'use strict';
    var oop = require('../lib/oop');
    var TextMode = require('./text').Mode;
    var LatexHighlightRules = require('./latex_highlight_rules').LatexHighlightRules;
    var CstyleBehaviour = require('./behaviour/cstyle').CstyleBehaviour;
    var LatexFoldMode = require('./folding/latex').FoldMode;
    var Mode = function () {
      this.HighlightRules = LatexHighlightRules;
      this.foldingRules = new LatexFoldMode();
      this.$behaviour = new CstyleBehaviour({ braces: true });
    };
    oop.inherits(Mode, TextMode);
    (function () {
      this.type = 'text';
      this.lineCommentStart = '%';
      this.$id = 'ace/mode/latex';
      this.getMatching = function (session, row, column) {
        if (row == undefined) row = session.selection.lead;
        if (typeof row == 'object') {
          column = row.column;
          row = row.row;
        }
        var startToken = session.getTokenAt(row, column);
        if (!startToken) return;
        if (startToken.value == '\\begin' || startToken.value == '\\end') {
          return this.foldingRules.latexBlock(session, row, column, true);
        }
      };
    }.call(Mode.prototype));
    exports.Mode = Mode;
  },
);
(function () {
  ace.require(['ace/mode/latex'], function (m) {
    if (typeof module == 'object' && typeof exports == 'object' && module) {
      module.exports = m;
    }
  });
})();
